package com.pan.kang;


import org.reflections.Reflections;

import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.EventListener;
import java.util.EventObject;
import java.util.Iterator;
import java.util.Set;
import java.util.function.Predicate;

public class PublisherExecutor {

    private final EventPublisher eventPublisher = new EventPublisher();

    private PublisherExecutor() {
        Reflections reflections = new Reflections("com.pan.kang");
        Set<Class<?>> set = reflections.getTypesAnnotatedWith(Event.class);
        Predicate<Class<?>> filter = cls -> {
            if (!Modifier.isInterface(cls.getModifiers()) && !Modifier.isAbstract(cls.getModifiers())) {
                return EventListener.class.isAssignableFrom(cls);
            }
            return false;
        };
        if (!set.isEmpty()) {
            set.stream().filter(filter).forEach(cls -> {
                try {
                    Object eventObject = cls.getDeclaredConstructor().newInstance();
                    if (eventObject instanceof Subscriber) {
                        eventPublisher.add((Subscriber<EventObject>) eventObject);
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
        }

    }

    private enum SinglePublisherExecutor {
        SINGLE_PUBLISHER_EXECUTOR;
        private final PublisherExecutor publisherExecutor;

        SinglePublisherExecutor() {
            publisherExecutor = new PublisherExecutor();
        }

        public PublisherExecutor getPublisherExecutor() {
            return publisherExecutor;
        }
    }

    public static PublisherExecutor build() {
        return SinglePublisherExecutor.SINGLE_PUBLISHER_EXECUTOR.getPublisherExecutor();
    }

    public void publishEvent(EventObject eventObject) {
        Iterator<Subscriber<EventObject>> iterator = eventPublisher.getSubscribers().iterator();
        while (iterator.hasNext()) {
            Subscriber<EventObject> subscriber = iterator.next();
            Class<?> clazz = subscriber.getClass();
            Type[] genericInterfaces = clazz.getGenericInterfaces();
            Type type = genericInterfaces[0];
            if (type instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                Type argument = parameterizedType.getActualTypeArguments()[0];
                if (((Class<?>) argument).isAssignableFrom(eventObject.getClass())) {
                    subscriber.handEvent(eventObject);
                }
            }


        }
    }
}
